<?php
/**
  * This is the base Controller class
* @author  Clinton Collins <clinton.collins@cgi.com>
* @todo  Implement the following
* @category Controller
* @since 1.0
*
*/

namespace EIP\C;

class ExceptionActionNotFound extends \Exception{};
class ExceptionTemplateNotFound extends \Exception{};

class Controllerbase {
	
	
	protected $pre_content_callbacks = array();
	
	protected $pre_header_callbacks = array();
	
	protected $post_footer_callbacks = array();
	protected $pre_footer_callbacks = array();
	
	/* 
	 * Javascript file to include in <script src tags in header
	 * 
	 */
	public $scripts = array();
	
	
	/* 
	 * Css file to include in header
	 * 
	 */
	public $styles = array();
	
	/*
	 * Contains a two dimensional array with the pre and post content templates
	 * 
	 * Contains a two dimensional array with the pre and post content templates.
	 * The first row(index 0) is shown before the content. The second row is shown
	 * after the content.
	 * Each row should be an array of templates, in display their respective display
	 * order
	 *
	*/
	public $layout = array(
			array('header.tpl'), //first row is executed before content
			array('footer.tpl') //this row is executed after content
		);
	
	/**
	 * Determines if the layout templates
	 * 
	 * Determines if the layout templates. In most cases you want to do this, but there are
	 * instances when you just want content printed, such as a templated javascript file,
	 * json data, dynamically loaded content(AJAX), etc.
	 */
	protected $layout_enabled = true;
	
	/**
	 * Toggles errors when a content_template is not found.
	 * 
	 * Toggles errors when a content_template is not found. You almost always want to alert yourself
	 * to the fact no template exists, but in some instances it is fine.
	 * For example, on JSON pages, it is much simplier to just print(json_encode($data)) then it
	 * is to add a template in the mix
	 */
	protected $ignore_content_template = false;
	
	public $disable_output = false;
	
	public $full_name = null;
	
	public $controller_scripts_enabled = true;
	
	public $controller_styles_enabled = true;
	
	
	public function __construct($full_name,$action) {
		$this->logger = \E::get_logger( ($full_name && strlen($full_name)>0) ? $full_name : get_class($this) );
		
		$this->action = $action;
		
		//load settings for controller
		\E::load_site_settings($full_name);
		
		
		$this->logger->debug("Initializing '".get_class($this)."'");
		
		$this->smarty = new \Smarty();
		$this->smarty->setTemplateDir(BASE_DIRECTORY.'/templates/');
		$this->smarty->setCompileDir(BASE_DIRECTORY.'/temp/templates_c/');
		$this->smarty->setCacheDir(BASE_DIRECTORY.'/temp/cache/');
		$this->smarty->setCaching(\Smarty::CACHING_LIFETIME_CURRENT);
		
		
		$this->full_name = $full_name;
		
		$this->logger->debug("Full Name: '$full_name'");

		//setup headers
		if ( AJAX )
			$this->layout_enabled = false;
		
		$this->logger->debug("Layouts Enabled '".$this->layout_enabled."'");
		
		$this->smarty->assignByRef('CONTROLLER',$this);
	}
	
	function __render() {
		
		$this->logger->debug("Beginning Render of '".get_class($this)."'");
		if($this->disable_output)
			return;
		$this->logger->debug("Starting Pre Header callbacks");
		foreach($this->pre_header_callbacks as $callback)
		{
			$callback($this);
		}
		
		$this->smarty->assignByRef('scripts',$this->scripts);
		$this->smarty->assignByRef('styles',$this->styles);
		
	
		//Example is Main/About ->Index action
		//do check for certain special file names
		
		//Controller Level Templated JS Resources
		//first off, we look for controller level scripts and css if feature enabled(it is by default)
		//check for templates/Main/About/js/About.js
		if($this->controller_scripts_enabled && $this->smarty->templateExists(sprintf('%s/js/%s.js',$this->full_name,get_class($this)))) {
			$this->logger->debug("Adding Controller Level Templated JS: '".sprintf('/%s/js/%s.js',$this->full_name,get_class($this))."' to header scripts." );
			$this->smarty->append('scripts',sprintf('%s/js/%s.js',\E::get_url($this->full_name),$this->action));
		}
		
		//Controller Level Templated CSS Resources
		//check for templates/Main/About/js/About.js
		if($this->controller_styles_enabled && $this->smarty->templateExists(sprintf('%s/css/%s.css',$this->full_name,get_class($this)))) {
			$this->logger->debug("Adding Controller Level Templated Css: '".sprintf('/%s/css/%s.css',$this->full_name,get_class($this))."' to header styles" );
			$this->smarty->append('scripts',sprintf('%s/css/%s.css',\E::get_url($this->full_name),$this->action));
		}
		
		//Controller Level JS Resources
		//check for resources/js/Main/About.js
		$this->logger->trace(sprintf('%s/resources/js/%s.js',BASE_DIRECTORY,$this->full_name));
		if($this->controller_scripts_enabled && file_exists(sprintf('%s/resources/js/%s.js',BASE_DIRECTORY,$this->full_name))) {
			$this->logger->debug("Adding Controller Level \"Static\" Js: '".sprintf('/resources/js/%s.js',$this->full_name)."' to header scripts" );
			$this->smarty->append(  'scripts',\E::get_raw_url(sprintf('resources/js/%s.js',$this->full_name))  );
		}
		
		//Controller Level CSS Resources
		//check for resources/css/Main/About.css
		if($this->controller_styles_enabled && file_exists(sprintf('%s/resources/css/%s.css',BASE_DIRECTORY,$this->full_name))) {
			$this->logger->debug("Adding Controller Level \"Static\" css: '".sprintf('/resources/css/%s.css',$this->full_name)."' to header styles" );
			$this->smarty->append(  'styles',\E::get_raw_url(sprintf('resources/css/%s.css',$this->full_name))  );
		}
		
		//Action Level Templated JS Scripts
		//check for templates/Main/About/js/About/Index.js
		if ($this->smarty->templateExists(sprintf('%s/js/%s.js',$this->getTemplateBaseName(),$this->action)))
		{
			$this->logger->debug("Adding Action Level Templated Js: '".sprintf('%s/%s/%s/js/%s.js',BASE_URL,get_class($this),$this->action)."' to header scripts" );
			$this->smarty->append('scripts',sprintf('%s/js/%s.js',\E::get_url($this->full_name,$this->action),$this->action) );
		}
		
		//Action Level Templated css 
		//check for templates/Main/About/css/About/Index.css
		if ($this->smarty->templateExists(sprintf('%s/css/%s.css',$this->getTemplateBaseName(),$this->action)))
		{
			$this->logger->debug("Adding Action Level Templated Css: '".sprintf('%s/%s/%s/css/%s.css',BASE_URL,get_class($this),$this->action)."' to header styles" );
			$this->smarty->append('scripts',sprintf('%s/css/%s.css',\E::get_url($this->full_name,$this->action),$this->action) );
		}
		
		//Action Level JS Resources
		//check for resources/js/Main/About/Index.js
		if(file_exists(sprintf('%s/resources/js/%s/%s.js',BASE_DIRECTORY,$this->full_name,$this->action))) {
			$this->logger->debug("Adding Action Level \"Static\" Js: '".sprintf('/resources/js/%s/%s.js',$this->full_name,$this->action)."' to header scripts" );
			$this->smarty->append(  'scripts',\E::get_raw_url( sprintf('resources/js/%s/%s.js',$this->full_name,$this->action) ) );
		}
		
		//Action Level CSS Resources
		//check for resources/css/Main/About/Index.css
		if(file_exists(sprintf('%s/resources/css/%s/%s.css',BASE_DIRECTORY,$this->full_name,$this->action))) {
			$this->logger->debug("Adding Action Level \"Static\" Css: '".sprintf('/resources/css/%s/%s.css',$this->full_name,$this->action)."' to header scripts" );
			$this->smarty->append(  'styles', \E::get_raw_url( sprintf('resources/css/%s/%s.css',$this->full_name,$this->action) )  );
		}
		$this->logger->debug("Layout Enabled? : ".$this->layout_enabled);
		
		if ($this->layout_enabled) { //do we print headers?
			//display the pre content templates
			foreach($this->layout[0] as $template)
			{
				$this->logger->debug("Displaying Header Template: '{$template}'");
				$this->smarty->display($template);
			}
			
		}
		$this->logger->debug("Starting Pre Content CallBacks");
		foreach($this->pre_content_callbacks as $callback)
			$callback($this);
		
		
		//content
		if($this->isStaticPage()) //handle static pages
		{
			$this->logger->debug("Static Page Content Rendering");
			if(\E::get_setting('static_page.caching',true))
			{
				$this->logger->trace("Using Cached static page");
				$this->smarty->setCaching(\Smarty::CACHING_LIFETIME_SAVED);
				$this->smarty->setCacheLifetime(-1);
				if(!$this->smarty->isCached('static_page.tpl',$this->static_page_id))
					$this->smarty->assign('static_page',$this->static_page);
				$this->smarty->display('static_page.tpl',$this->static_page_id);
			} else {
				$this->smarty->setCaching(\Smarty::CACHING_OFF);
				$this->logger->trace("Static Page Caching disabled, bypassing....");
				$this->smarty->assign('static_page',$this->static_page);
				$this->smarty->display('static_page.tpl');
			}
		}
		else {
			$this->logger->debug("Rendering dynamic content");
			try {
				$template = $this->getTemplate();
				$this->logger->debug("Rendering from user created template: {$template}");
				
				if(!\E::get_setting('action.caching',false))
					$this->smarty->setCaching(\Smarty::CACHING_OFF);
				
				$this->smarty->display($template);
			} catch(ExceptionTemplateNotFound $e) {
				if (!$this->ignore_content_template) {
					$this->logger->error("Could not find template");
					throw $e; //retrhow because we do care
				}
			}
		}
		$this->smarty->setCaching(\Smarty::CACHING_LIFETIME_CURRENT);
		//set the render variable now...
		$rtime = round( (microtime(true) - EIP_START), 4);
		$this->logger->info("Render Time: ".$rtime);
		$this->smarty->assign('EIP_render_time',$rtime);
		
		$this->logger->debug("Starting Pre Footer Callbacks");
		
		foreach($this->pre_footer_callbacks as $callback)
			$callback($this);
		
		if ($this->layout_enabled) { //do we print footers?
			//display the prost content templates
			foreach($this->layout[1] as $template)
			{
				$this->smarty->display($template);
			}
		}
		$this->logger->debug("Starting Post Footer Callbacks");
		
		foreach($this->post_footer_callbacks as $callback)
			$callback($this);
		$this->logger->debug("Done with render");
	}

	public function setContentType($type) {
		switch(strtolower($type)) {
			case "css":
				$type="text/css";
			break;
			case "js":
				$type="text/javascript";
				break;
		}
		switch(strtolower($type)) {
			
			case "text/css":
			case "text/javascript":
				$this->layout_enabled=false;
				break;
		}
		$this->logger->info("Setting Content-type: {$type}");
		header("Content-type: {$type}");
	}
	public function isStaticPage() {
		return isset($this->static_page);
	}

	protected function getTemplate() {
		if(!isset($this->template_name)) {
			//examples are for Main/About (which translate to Main/About.php->Index())
			//look for templates/{full_name}.ajax.tpl
			//so templates/Main/About/Index.ajax.tpl
			if(AJAX && $this->smarty->templateExists( sprintf( '%s.ajax.tpl',$this->getTemplateBaseName() ) ) )
				$this->template_name = sprintf('%s.ajax.tpl',$this->getTemplateBaseName() );
			//look for templates/Main/About/Index.tpl next
        	else if($this->smarty->templateExists( sprintf( '%s.tpl',$this->getTemplateBaseName() ) ) )
			 	$this->template_name = sprintf('%s.tpl',$this->getTemplateBaseName() );
			else if($this->smarty->templateExists( $this->getTemplateBaseName() ) )
				$this->template_name = $this->getTemplateBaseName();
		}
		if(isset($this->template_name))
			return $this->template_name;
		throw new ExceptionTemplateNotFound( sprintf('Cannot locate template for "%s"->"%s"', $this->full_name, strtolower($this->action)) );
	}
	
	protected function getTemplateBaseName() {
		//would rather use get class name, but we prefer full path controller names for this function
		$this->logger->trace(sprintf("%s/%s",$this->full_name, $this->action));
		if(!isset($this->base_template_name))
			$this->base_template_name = sprintf("%s/%s",$this->full_name, $this->action);
		return $this->base_template_name;
	}
	
	 /**
     *
     * Called anytime you attempt to call class method from controller. For example $controller->Test() would call this method with the arguments 
	* $method = Test, $args = null
	* We override this to provide error callbacks, as well as catch special scenarios where we may try to call a method that may not exist to trigger templates. 
	* A good example of this is .js/css Method. Any method name ending in .js/.css is assumed to be Javascript and CSS, and this will set the header to 
	* correct content-type as well as display the js template. This is useful to autoinclude .js.tpls templates,
     *
     * @param string    $method  Name of class method to call
     * @param string    $args  Arguments to pass to function
     */
	public function __call($method, $args) 
	{
		$this->title = preg_replace("#/#",' : ',$this->full_name ).' : '.$method;
		if (method_exists($this, $method)) {
			$this->logger->debug("Found Action: '{$method} Executing"); 
          	return call_user_func_array(array($this, $method), $args); 
			
		
        } else if (  $this->smarty->templateExists( $this->getTemplateBaseName() ) || $this->smarty->templateExists( sprintf("%s/%s",$this->getTemplateBaseName(),$method) )  ) {
        	//just return.. assume no stuff is needed
        	$this->logger->debug("Found Template for Action '{$method}' even though no method."); 
        	if(preg_match('#.*?\.(js|css)$#i',$method,$matches))
			{
				$this->setContentType(strtolower($matches[1]));
			}
        	return true;
        }  else {
        	$spid = md5($this->full_name.'.'.strtolower($method));
			$this->logger->trace("SPID: '{$spid}'");
			if(\E::get_setting('static_page.caching',true))
			{
				//check for cached copy of a static page with that name
				if($this->smarty->isCached('static_page.tpl', $spid))
				{
					$this->static_page_id = $spid; 
					$this->static_page = true; //let controlelr know it is a static page
					$this->logger->debug("Using Cached copy of static page: {$spid}");
					$this->logger->info("Found CACHED Static Page for '{$this->full_name}'->'{$method}()' ");
					return true;
				} else {
					return $this->__load_static_page($method,$spid);
				}
			} else {
	        	return $this->__load_static_page($method,$spid);
			}
			

        	$this->logger->error("Cannot Find Action or Template for: '{$this->full_name}'->'{$method}()'");
			throw new ExceptionActionNotFound(sprintf('The required method "%s" does not exist for \'%s\'', $method, get_class($this)));
		}

	}

	private function __load_static_page(&$method,$spid) {
		\E::loadRedBean();
    	$static_page = \R::findOne('static_page', ' ( controller = :controller AND action = :action ) ', 
    		array(
    		':controller' => $this->full_name,
    		':action' => strtolower($method)
			) 
		);
		if($static_page) {
			$this->logger->info("Found NONCACHED Static Page for '{$this->full_name}'->'{$method}()' ");
			$this->static_page_id = $spid; 
    		$this->static_page = $static_page;
			
			return true;
		}
		throw new ExceptionActionNotFound(sprintf('The required method "%s" does not exist for \'%s\'', $method, get_class($this)));
	}
}
?>